/* 
 * Copyright 2012-2013 Ceruti Francesco & contributors
 * 
 * This file is part of LiSP (Linux Show Player).
 * 
 * 
 * gcc -Wall -fPIC -shared -I/usr/include/python3.3m/ audiowave.c -o audiowave.so -lsox
 * */

#include <sox.h>
#include <stdio.h>
#include <Python.h>

static PyObject* read_audio_info(PyObject *self, PyObject *args)
{
	const char* infile;

	if (!PyArg_ParseTuple(args, "s", &infile))
		return NULL;

	sox_format_init();
	sox_format_t* input = sox_open_read(infile, NULL, NULL, NULL);

	// Create a python dict
	PyObject* nfo = PyDict_New();	
	PyDict_SetItemString(nfo, "nchannels", PyLong_FromLong((long)input->signal.channels));
	PyDict_SetItemString(nfo, "nframes", PyLong_FromLong((long)input->signal.length));
	PyDict_SetItemString(nfo, "framerate", PyLong_FromLong((long)input->signal.rate));
	PyDict_SetItemString(nfo, "samplewidth", PyLong_FromLong((long)input->signal.precision / 8)); // bit -> byte

	return nfo;
}

static PyObject* read_audio_minmax(PyObject *self, PyObject *args)
{
	int max, min;
	int mul;			// Multiply the (normalized) signal for this value
	int chunk_size;		// Size of readed chunks (number of samples)
	char* infile;
	unsigned int i;

	sox_format_t * input;		// Sox input
	sox_sample_t * frames;		// Buffer for readed samples
	size_t frames_size, count;	// Frames size and

	PyObject* peaks = PyList_New(0); // Python list to return

	// Parse args from python
	if (!PyArg_ParseTuple(args, "s|i|i", &infile, &chunk_size, &mul))
		return NULL;

	// Init sox and open the file
	sox_format_init();
	input = sox_open_read(infile, NULL, NULL, NULL);
	// Make the number of samples to be read proportional to the number of channels
	chunk_size *= input->signal.channels;

	// Allocate memory to read from library
	frames_size = sizeof(sox_sample_t) * chunk_size;
	frames = (sox_sample_t *) malloc(frames_size);

	// Reads all the pieces and for each one calculates the average
	count = chunk_size;	
	while ((int) count == chunk_size){
		count = sox_read(input, frames, chunk_size);

		max = SOX_SAMPLE_MIN; // Maximum sample value
		min = SOX_SAMPLE_MAX; // Minimum sample value

		for (i=0; i<count; i++)
		{
			if(frames[i] < min) min = frames[i];
			else if(frames[i] > max) max = frames[i];
		}

		PyObject* minmax = PyTuple_New(2);
		PyTuple_SetItem(minmax, 0, PyFloat_FromDouble((double) min / SOX_SAMPLE_MAX * mul));
		PyTuple_SetItem(minmax, 1, PyFloat_FromDouble((double) max / SOX_SAMPLE_MAX * mul));

		// Append the value to a python list
		PyList_Append(peaks, minmax);
	}

	free(frames);

	return peaks;
}

static PyObject* read_audio_rms(PyObject *self, PyObject *args)
{
	int mul;			// Multiply the (normalized) signal for this value
	int chunk_size;		// Size of readed chunks (number of samples)
	char* infile;
	long double sum_squares;
	unsigned int i;

	sox_format_t * input;		// Sox input
	sox_sample_t * frames;		// Buffer for readed samples
	size_t frames_size, count;	// Frames size and

	PyObject* rms = PyList_New(0); // Python list to return

	// Parse args from python
	if (!PyArg_ParseTuple(args, "s|i|i", &infile, &chunk_size, &mul))
		return NULL;

	// Init sox and open the file
	sox_format_init();
	input = sox_open_read(infile, NULL, NULL, NULL);
	// Make the number of samples to be read proportional to the number of channels
	chunk_size *= input->signal.channels;

	// Allocate memory to read from library
	frames_size = sizeof(sox_sample_t) * chunk_size;
	frames = (sox_sample_t *) malloc(frames_size);

	// Reads all the chunks and for each one calculates the average
	count = chunk_size;	
	while ((int) count == chunk_size){
		count = sox_read(input, frames, chunk_size);

		sum_squares = 0.0;

		// Sum the values
		for (i=0; i<count; i++)
		{
			sum_squares += (long double)frames[i]*(long double)frames[i];
		}

		if(count != 0) sum_squares = (int)sqrt(sum_squares / (double) count);

		// Append the value to a python list
		PyList_Append(rms, PyFloat_FromDouble((double) sum_squares / SOX_SAMPLE_MAX * mul));
	}

	free(frames);

	return rms;
}

static PyObject* read_audio_minmax_rms(PyObject *self, PyObject *args)
{
	int max, min, mul, chunk_size;
	char* infile;
	long double sum_squares;
	unsigned int i;

	sox_format_t * input;
	sox_sample_t * frames;
	size_t frames_size, count;

	PyObject* rms = PyList_New(0);
	PyObject* peaks = PyList_New(0);
	PyObject* result = PyTuple_New(2);	

	if (!PyArg_ParseTuple(args, "s|i|i", &infile, &chunk_size, &mul))
		return NULL;

	sox_format_init();
	input = sox_open_read(infile, NULL, NULL, NULL);

	chunk_size *= input->signal.channels;

	frames_size = sizeof(sox_sample_t) * chunk_size;
	frames = (sox_sample_t *) malloc(frames_size);

	count = chunk_size;	
	while ((int) count == chunk_size){
		count = sox_read(input, frames, chunk_size);

		max = SOX_SAMPLE_MIN;
		min = SOX_SAMPLE_MAX;
		sum_squares = 0.0;

		for (i=0; i<count; i++)
		{
			sum_squares += (long double)frames[i]*(long double)frames[i];
			
			if(frames[i] < min) min = frames[i];
			else if(frames[i] > max) max = frames[i];
		}

		if(count != 0) sum_squares = (int)sqrt(sum_squares / (double) count);
		
		PyObject* minmax = PyTuple_New(2);
		PyTuple_SetItem(minmax, 0, PyFloat_FromDouble((double) min / SOX_SAMPLE_MAX * mul));
		PyTuple_SetItem(minmax, 1, PyFloat_FromDouble((double) max / SOX_SAMPLE_MAX * mul));

		PyList_Append(rms, PyFloat_FromDouble((double) sum_squares / SOX_SAMPLE_MAX * mul));
		PyList_Append(peaks, minmax);
	}

	free(frames);
	
	PyTuple_SetItem(result, 0, peaks);
	PyTuple_SetItem(result, 1, rms);	

	return result;
}

/*  Python interfacing functions */

static PyMethodDef AudioWaveMethods[] = {
    {"read_audio_peaks", read_audio_minmax, METH_VARARGS, ""},
    {"read_audio_rms", read_audio_rms, METH_VARARGS, ""},
    {"read_audio_peaks_rms", read_audio_minmax_rms, METH_VARARGS, ""},
    {"read_audio_info", read_audio_info, METH_VARARGS, ""},
    {NULL, NULL, 0, NULL}        /* Sentinel */
};

static struct PyModuleDef audiowave = {
   PyModuleDef_HEAD_INIT,
   "audiowave",			/* name of module */
   NULL,				/* module documentation, may be NULL */
   -1,       			/* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */
   AudioWaveMethods
};

PyMODINIT_FUNC
PyInit_audiowave(void)
{
    return PyModule_Create(&audiowave);
}
